
#import "BYRequest.h"
#import "BYResponse.h"
#import "NSString+UDCategory.h"
#import "NSObject+SBJson.h"
#import "Reachability.h"
#import "BYNetwork.h"
#import "FMDatabase.h"

@interface BYRequest ()



@end

@implementation BYRequest

@synthesize resultDelegate=_resultDelegate;
@synthesize userInfo=_userInfo;
@synthesize requestCache=_requestCache;
@synthesize responseCache=_responseCache;
@synthesize cookieStr=_cookieStr;


- (id)init {
    self = [super init];
    if (self) {
        _userInfo = [[NSDictionary alloc]init];
        _requestCache = YES;
    }
    return self;
}

- (void)buildRequest {
    
    NSString *URLString = [NSString stringWithFormat:@"%@%@", [self apiURLPrefix], [self apiURLSuffix]];
    NSString *escapedURLString = [URLString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    
    id fileValue=nil;
    NSDictionary *parametersDic = [self parameters];
    
    NSMutableDictionary *paramMutDict = [NSMutableDictionary dictionaryWithDictionary:parametersDic];
    
    //extract file parameters
    if ([[self apiRequestMethod] isEqualToString:@"POST"]) {
        fileValue = [paramMutDict valueForKey:@"Filedata"];
        [paramMutDict removeObjectForKey:@"Filedata"];
        
    }
    
    [super buildWithURLString:escapedURLString params:paramMutDict httpMethod:[self apiRequestMethod]];
    // [super buildWithURLString:escapedURLString params:paramMutDict cookie:self.cookieStr httpMethod:[self apiRequestMethod]];
    
    //add upload files
    if (fileValue) {
        [self addFile:fileValue forKey:@"Filedata"];
    }
}

//parameters after "?" for GET, post parameters for POST
//if you need paramters after "?" for POST, put them in apiURLSuffix as string like "?a=b"
- (NSMutableDictionary *)parameters {
    NSMutableDictionary *parametersDic = [[NSMutableDictionary alloc] initWithCapacity:10];
    
    [parametersDic setValue:@"json" forKey:@"format"];
    
    return parametersDic;
}

- (BOOL)validateParameters:(NSMutableString *)errorMessage {
    return YES;
}

- (void)sendAsynchronous {
    NSMutableString *errorMessage = [NSMutableString string];
    BOOL validateResult = [self validateParameters:errorMessage];
    if (validateResult) {
        [self buildRequest];
        //if network unavailable, use cache
        if ([Reachability reachabilityForInternetConnection].currentReachabilityStatus==NotReachable) {
            _requestCache=YES;
        }
        
        if(self.requestCache){
            [NSThread detachNewThreadSelector:@selector(requestFromCache:) toTarget:self withObject:self];
        }
        else {
            [self startAsynchronous];
        }
    }
    else {
        BYResponse *errorResponse = [[BYResponse alloc] initWithErrorMessage:errorMessage];
        if(self.finishBlock){
            self.finishBlock(self,errorResponse);
        }else{
            [self.resultDelegate request:self didEndWithResponse:errorResponse];
        }
    }
}

- (void)startAsynchronous
{
    BYRequest * __unsafe_unretained blockParam=self;
    
    [self onCompletion:^(MKNetworkOperation *completedOperation)
     {
         NSString *dataFrom=@"Online";
         if([completedOperation isCachedResponse]) {
             dataFrom=@"Cache";
         }
         else {
             dataFrom=@"Online";
         }
         DLog(@"%@:\n%@", dataFrom,completedOperation);
         
         [blockParam requestFinished:blockParam];
         
     }onError:^(NSError* error) {
         
         [blockParam requestFailed:blockParam];
     }];//operationWithPath
    
    [[[BYNetwork sharedNetwork] sharedNetworkEngine] enqueueOperation:(MKNetworkOperation *)self];
}

- (void)sendSynchronous {
    [self sendAsynchronous];
}

- (NSString *)apiURLPrefix {
    return NETWORK_URL_PREFIX;
}

- (NSString *)apiURLSuffix {
    return @"/";
}

- (NSString *)apiRequestMethod {
    return @"GET"; //or POST for file upload
}

- (Class)responseClass {
    return [BYResponse class];
}

- (NSString *)responseString
{
	NSString *respStr = [super responseString];
    if (!respStr ||[respStr length]<=0) {
        return _responseCache;
    }
    return respStr;
}

#pragma mark Network complete

- (void)requestFinished:(BYRequest *)currentRequest {
    if (currentRequest == self) {
        
        [self updateCacheData:self];
        
        id jasonValue = self.responseString.JSONValue;
        if (NULL==jasonValue) {
            jasonValue=[NSDictionary dictionary];
        }
        BYResponse *response = [[[self responseClass] alloc] initWithResponseDictionary:jasonValue];
        
        if([self.responseJSON isKindOfClass:[NSDictionary class]]){
            NSString *timeOut = [self.responseJSON objectForKey:@"timeout"];
            if(timeOut !=nil && [timeOut intValue]==1){
                //弹出到登陆界面
                [[NSNotificationCenter defaultCenter] postNotificationName:@"timeout" object:nil];
                
            }
            else{
                
            }
        }
        if(self.finishBlock){
            self.finishBlock(self,response);
        }
        else{
            [self.resultDelegate request:self didEndWithResponse:response];
        }

        
    }
}

- (void)requestFailed:(BYRequest *)currentRequest {
    
    if (currentRequest == self) {
        
        NSString *errorMessage = currentRequest.error.localizedDescription;
        if (nil==errorMessage) {
            errorMessage = [NSString stringWithFormat:@"%@", NSLocalizedString(@"Network failure, please try again", nil)];
        }
        
        if([self.responseJSON isKindOfClass:[NSDictionary class]]){
            if([self.responseJSON isKindOfClass:[NSDictionary class]]){
                NSString *timeOut = [self.responseJSON objectForKey:@"timeout"];
                if(timeOut !=nil && [timeOut intValue]==1){
                    //弹出到登陆界面
                    [[NSNotificationCenter defaultCenter] postNotificationName:@"timeout" object:nil];
                    
                }
                else{
                    
                }
            }
        }
        BYResponse *response = [[[self responseClass] alloc] initWithErrorMessage:errorMessage];
        if(self.finishBlock){
                   self.finishBlock(self,response);
               }
        else{
            [self.resultDelegate request:self didEndWithResponse:response];
        }
    }
}

-(id) responseJSON
{
    //request from cache
    if (self.requestCache) {
        
        if (self.responseCache) {
            return self.responseCache.JSONValue;
        }
        //no cache match, result is from server
        return self.responseString.JSONValue;
        
    }
    
    return [super responseJSON];
}
#pragma mark Cache Methods
- (BOOL)isLocalCache {
    return [BYNetwork sharedNetwork].localCacheFlag;
}

- (NSString*)cacheKey {
    NSMutableString* cacheKeyString = [NSMutableString string];
    [cacheKeyString appendString:self.url];
    NSDictionary *parametersDic = [self parameters];
    NSArray *keyArray = [parametersDic allKeys];
    for (NSString *key in keyArray) {
        id value = [parametersDic valueForKey:key];
        [cacheKeyString appendFormat:@"&%@=%@",key,value];
    }
    
    NSString *cacheKeyHash = [cacheKeyString md5Hash];
    return cacheKeyHash;
}

- (BOOL)clearCache {
    //if netowrk unavailable, keep cache
    if ([Reachability reachabilityForInternetConnection].currentReachabilityStatus==NotReachable) {
        return NO;
    }
    @autoreleasepool {
        NSString *cacheDomain = NSStringFromClass(self.class);
        NSString *cacheKey = [self cacheKey];
        NSString *cachePath = [NSString stringWithFormat:@"%@/Library/%@",NSHomeDirectory(),@"BYCache.db"];
        FMDatabase *db = [FMDatabase databaseWithPath:cachePath];
        [db open];
        if ([db hadError]) {
            DLog(@"Err %d: %@", [db lastErrorCode], [db lastErrorMessage]);
            return NO;
        }
        
        [db executeUpdate:@"delete from BYCache where cacheDomain=? and cacheKey=? ", cacheDomain, cacheKey];
        if ([db hadError]) {
            DLog(@"Err %d: %@", [db lastErrorCode], [db lastErrorMessage]);
        }
        
        [db close];
        return YES;
    }
}

+ (BOOL)clearCache {
    //if netowrk unavailable, keep cache
    if ([Reachability reachabilityForInternetConnection].currentReachabilityStatus==NotReachable) {
        return NO;
    }
    @autoreleasepool {
        NSString *cacheDomain = NSStringFromClass(self.class);
        NSString *cachePath = [NSString stringWithFormat:@"%@/Library/%@",NSHomeDirectory(),@"BYCache.db"];
        FMDatabase *db = [FMDatabase databaseWithPath:cachePath];
        [db open];
        if ([db hadError]) {
            DLog(@"Err %d: %@", [db lastErrorCode], [db lastErrorMessage]);
            return NO;
        }
        
        [db executeUpdate:@"delete from BYCache where cacheDomain=?  ", cacheDomain];
        if ([db hadError]) {
            DLog(@"Err %d: %@", [db lastErrorCode], [db lastErrorMessage]);
        }
        
        [db close];
        return YES;
    }
}

+ (BOOL)clearAppCache {
    //if netowrk unavailable, keep cache
    if ([Reachability reachabilityForInternetConnection].currentReachabilityStatus==NotReachable) {
        return NO;
    }
    @autoreleasepool {
        NSString *cachePath = [NSString stringWithFormat:@"%@/Library/%@",NSHomeDirectory(),@"BYCache.db"];
        FMDatabase *db = [FMDatabase databaseWithPath:cachePath];
        [db open];
        if ([db hadError]) {
            DLog(@"Err %d: %@", [db lastErrorCode], [db lastErrorMessage]);
            return NO;
        }
        
        [db executeUpdate:@"delete from BYCache "];
        if ([db hadError]) {
            DLog(@"Err %d: %@", [db lastErrorCode], [db lastErrorMessage]);
        }
        
        [db close];
        return YES;
    }
}

- (void)requestFromCache:(BYRequest*)aRequest {
    @autoreleasepool {
        NSString *cacheDomain = NSStringFromClass(aRequest.class);
        NSString *cacheKey = [aRequest cacheKey];
        NSString *cachePath = [NSString stringWithFormat:@"%@/Library/%@",NSHomeDirectory(),@"BYCache.db"];
        FMDatabase *db = [FMDatabase databaseWithPath:cachePath];
        [db open];
        if ([db hadError]) {
            DLog(@"Err %d: %@", [db lastErrorCode], [db lastErrorMessage]);
            //[self startAsynchronous];
            [self performSelectorOnMainThread:@selector(startAsynchronous) withObject:nil waitUntilDone:NO];
            return;
        }
        
        [db executeUpdate:@"create table if not exists BYCache (cacheDomain varchar(100),cacheKey char(40),cacheContent BLOB,cacheTime INTEGER)"];
        
        FMResultSet *rs = [db executeQuery:@"select cacheContent, length(cacheContent) as cacheLength from BYCache where cacheDomain=? and cacheKey=? ", cacheDomain, cacheKey];
        
        while ([rs next]) {
            /*
             NSData *cacheData=[rs dataForColumn:@"cacheContent"];
             NSString *retStr = [[NSString alloc]initWithData:cacheData encoding:NSUnicodeStringEncoding];
             aRequest.responseCache = retStr;
             */
            aRequest.responseCache = [rs stringForColumn:@"cacheContent"];
        }
        [rs close];
        [db close];
        
        
        if (aRequest.responseCache==nil){  //no cache found, use network
            [self performSelectorOnMainThread:@selector(startAsynchronous) withObject:nil waitUntilDone:NO];
        }
        else {
            [self performSelectorOnMainThread:@selector(requestCacheFinished:) withObject:aRequest waitUntilDone:NO];
        }
    }
}

- (void)updateCacheData:(BYRequest*)aRequest {
    
    @autoreleasepool {
        NSString *cacheDomain = NSStringFromClass(aRequest.class);
        NSString *cacheKey = [aRequest cacheKey];
        NSString *cacheContent = aRequest.responseString;
        NSTimeInterval cacheTime = [[NSDate date] timeIntervalSince1970];
        NSString *cachePath = [NSString stringWithFormat:@"%@/Library/%@",NSHomeDirectory(),@"BYCache.db"];
        FMDatabase *db = [FMDatabase databaseWithPath:cachePath];
        
        [db open];
        if ([db hadError]) {
            DLog(@"Err %d: %@", [db lastErrorCode], [db lastErrorMessage]);
            return;
        }
        //db has a trigger to remove old caches
        [db executeUpdate: @"insert into BYCache values(?,?,?,?)" , cacheDomain,cacheKey,cacheContent,[NSNumber numberWithDouble:cacheTime]];
        if ([db hadError]) {
            DLog(@"Err %d: %@", [db lastErrorCode], [db lastErrorMessage]);
        }
        [db close];
    }
}
- (void)requestCacheFinished:(BYRequest *)aRequest {
    BYResponse *response;
    if (aRequest.responseCache!=nil){
        response = [[[aRequest responseClass] alloc] initWithResponseDictionary:
                    aRequest.responseCache.JSONValue];
        response.isCache = YES;
    }
    else {
        response = [[[aRequest responseClass] alloc] initWithResponseDictionary:
                    aRequest.responseString.JSONValue];
        response.isCache = NO;
    }
    
    if([self.responseJSON isKindOfClass:[NSDictionary class]]){
        NSString *timeOut = [self.responseJSON objectForKey:@"timeout"];
        if(timeOut !=nil && [timeOut intValue]==1){
            [[NSNotificationCenter defaultCenter] postNotificationName:@"timeout" object:nil];
            
        }
        else{
            
        }
    }
    if(self.finishBlock){//block 形式
       self.finishBlock(self,response);
    }
    else{
        //delegte　形式
        [aRequest.resultDelegate request:aRequest didEndWithResponse:response];
    }

}

@end
